import { readUriToTextAndExplode } from "../readFileToText/openTextFile";
import { Coordinate } from "../coordParser/Coordinate";
import { ISctVorNdb } from "../interfaces/ISctVorNdb";
import { IGroupedLines } from "../interfaces/IGroupedLines";
import { ICoordArrayWithFlag, ILineWithFlag, Line } from "../interfaces/Line";
import { ISctGeo } from "../interfaces/ISctGeo";
import { ISctRegion } from "../interfaces/ISctRegion";
import { RenderData } from "../classes/RadarRenderData";

const pureNumRegex: RegExp = /^[0-9]*$/;

export function parseNum2HexColor(num: string) {
    return '#' + Number(num).toString(16).padStart(6, '0');
}

export function parseColorFlag(flag: string): string {
    if (pureNumRegex.test(flag)) {
        return parseNum2HexColor(flag);
    } else {
        const v = RenderData.sct.definition.get(flag);
        if (v && pureNumRegex.test(v)) {
            return parseNum2HexColor(v);
        } else {
            return flag;
        }
    }
}

export async function loadSctFile(uri: string) {
    const lines = await readUriToTextAndExplode(uri, ';');
    let vorReadingFlag = false;
    let ndbReadingFlag = false;
    let aptReadingFlag = false;
    let rwyReadingFlag = false;
    let fixReadingFlag = false;
    let artccReadingFlag = false;
    let artccHighReadingFlag = false;
    let artccLowReadingFlag = false;
    let sidReadingFlag = false;
    let startReadingFlag = false;
    let lawReadingFlag = false;
    let hawReadingFlag = false;
    let geoReadingFlag = false;
    let regReadingFlag = false;
    let activeRegionGroup: ISctRegion | null = null;
    let activeGeoGroup: ISctGeo | null = null;
    let activeSidStarGroup: string = '';
    const resetFlag = () => {
        vorReadingFlag = false;
        ndbReadingFlag = false;
        aptReadingFlag = false;
        rwyReadingFlag = false;
        fixReadingFlag = false;
        artccHighReadingFlag = false;
        artccReadingFlag = false;
        artccLowReadingFlag = false;
        sidReadingFlag = false;
        startReadingFlag = false;
        lawReadingFlag = false;
        hawReadingFlag = false;
        geoReadingFlag = false;
        regReadingFlag = false;
    }
    const definitionRegex: RegExp = new RegExp('#define\\s+(?<key>\\S+)\\s+(?<value>\\S+)');
    const namedRadioFixRegex: RegExp = new RegExp('(?<name>\\S+)\\s+(?<radio>\\d{3}.\\d{3})\\s+(?<coord1>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord2>[NSEW]\\d+.\\d+.\\d+.\\d+)');
    const namedFixRegex: RegExp = new RegExp('(?<name>\\S+)\\s+(?<coord1>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord2>[NSEW]\\d+.\\d+.\\d+.\\d+)');
    const airwayRegex: RegExp = new RegExp('(?<group>\\S+)\\s+(?<coord1>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord2>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord3>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord4>[NSEW]\\d+.\\d+.\\d+.\\d+)');
    const airwayReferFormat: RegExp = new RegExp('(?<group>\\S+)\\s+(?<refer1>\\S+)\\s+(?<refer2>\\S+)\\s+(?<refer3>\\S+)\\s+(?<refer4>\\S+)');
    const airportRegex: RegExp = new RegExp('(?<icao>\\S+)\\s+(?<frequency>\\d{3}.\\d{3})\\s+(?<coord1>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord2>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<class>\\S+)');
    const runwayRegex: RegExp = new RegExp('(?<ep1>\\S+)\\s+(?<ep2>\\S+)\\s+(?<h1>\\d*)\\s+(?<h2>\\d*)\\s+(?<coord1>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord2>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord3>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord4>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<code>\\S+)\\s+(?<name>\\S+)')
    const sidStarEntryRegex: RegExp = new RegExp('(?<group>\\S+)\\s+(?<coord1>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord2>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord3>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord4>[NSEW]\\d+.\\d+.\\d+.\\d+)');
    const sidStarAppendRegex: RegExp = new RegExp('^(?<coord1>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord2>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord3>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord4>[NSEW]\\d+.\\d+.\\d+.\\d+)');
    const geoEntryRegex: RegExp = new RegExp('(?<group>\\S+)\\s+(?<coord1>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord2>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord3>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord4>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<flag>\\S+)');
    const geoAppendRegex: RegExp = new RegExp('(?<coord1>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord2>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord3>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord4>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<flag>\\S+)');
    const regEntryRegex: RegExp = new RegExp('(?<name>\\S+)\\s+(?<flag>\\S+)\\s+(?<coord1>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord2>[NSEW]\\d+.\\d+.\\d+.\\d+)');
    const regSingleNameEntryRegex: RegExp = new RegExp('REGIONNAME\\s+(?<name>\\S+)');
    const regItemAppendRegex: RegExp = new RegExp('(?<flag>\\S+)\\s+(?<coord1>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord2>[NSEW]\\d+.\\d+.\\d+.\\d+)');
    const regCoordAppendRegex: RegExp = new RegExp('(?<coord1>[NSEW]\\d+.\\d+.\\d+.\\d+)\\s+(?<coord2>[NSEW]\\d+.\\d+.\\d+.\\d+)');
    for (let line of lines) {
        const tag = line.toUpperCase();
        if (tag == '[VOR]') {
            resetFlag();
            vorReadingFlag = true;
            continue;
        } else if (tag == '[NDB]') {
            resetFlag();
            ndbReadingFlag = true;
            continue;
        } else if (tag == '[FIXES]') {
            resetFlag();
            fixReadingFlag = true;
            continue;
        } else if (tag == '[AIRPORT]') {
            resetFlag();
            aptReadingFlag = true;
            continue;
        } else if (tag == '[RUNWAY]') {
            resetFlag();
            rwyReadingFlag = true;
            continue;
        } else if (tag == '[SID]') {
            resetFlag();
            sidReadingFlag = true;
            continue;
        } else if (tag == '[STAR]') {
            resetFlag();
            startReadingFlag = true;
            continue;
        } else if (tag == '[ARTCC HIGH]') {
            resetFlag();
            artccHighReadingFlag = true;
            continue;
        } else if (tag == '[ARTCC]') {
            resetFlag();
            artccReadingFlag = true;
            continue;
        } else if (tag == '[ARTCC LOW]') {
            resetFlag();
            artccLowReadingFlag = true;
            continue;
        } else if (tag == '[GEO]') {
            resetFlag();
            geoReadingFlag = true;
            continue;
        } else if (tag == '[REGIONS]') {
            resetFlag();
            regReadingFlag = true;
            continue;
        } else if (tag == '[HIGH AIRWAY]') {
            resetFlag();
            hawReadingFlag = true;
            continue;
        } else if (tag == '[LOW AIRWAY]') {
            resetFlag();
            lawReadingFlag = true;
            continue;
        }
        if (definitionRegex.test(line)) {
            const match = definitionRegex.exec(line);
            if (match != null && match.groups != undefined) {
                const g = match.groups;
                const key = g.key;
                const value = g.value;
                if (key != undefined && value != undefined) {
                    RenderData.sct.definition.set(g.key, g.value);
                }
            }
        } else if ((vorReadingFlag || vorReadingFlag) && namedRadioFixRegex.test(line)) {
            const match = namedRadioFixRegex.exec(line);
            if (match != null && match.groups != undefined) {
                const g = match.groups;
                const target: ISctVorNdb = {
                    name: g.name,
                    frequency: g.radio,
                    coord: new Coordinate().setFromStr(g.coord1).setFromStr(g.coord2),
                };
                if (vorReadingFlag) {
                    RenderData.sct.vor.set(g.name, target);
                } else if (ndbReadingFlag) {
                    RenderData.sct.ndb.set(g.name, target);
                }
            }
        } else if (fixReadingFlag && namedFixRegex.test(line)) {
            const match = namedFixRegex.exec(line);
            if (match != null && match.groups != undefined) {
                const g = match.groups;
                RenderData.sct.fix.set(g.name, {
                    name: g.name,
                    coord: new Coordinate().setFromStr(g.coord1).setFromStr(g.coord2),
                });
            }
        } else if (lawReadingFlag || hawReadingFlag) {
            if (airwayRegex.test(line)) {
                const match = airwayRegex.exec(line);
                if (match != null && match.groups != undefined) {
                    const g = match.groups;
                    const target: IGroupedLines = {
                        group: g.group,
                        lines: [
                            [
                                new Coordinate().setFromStr(g.coord1).setFromStr(g.coord2),
                                new Coordinate().setFromStr(g.coord3).setFromStr(g.coord4)
                            ]
                        ]
                    }
                    if (lawReadingFlag) {
                        if (RenderData.sct.loAirway.has(g.group)) {
                            const orig = RenderData.sct.loAirway.get(g.group)!;
                            orig.lines.push(target.lines[0]);
                        } else {
                            RenderData.sct.loAirway.set(g.group, target);
                        }
                    } else if (hawReadingFlag) {
                        if (RenderData.sct.hiAirway.has(g.group)) {
                            const orig = RenderData.sct.hiAirway.get(g.group)!;
                            orig.lines.push(target.lines[0]);
                        } else {
                            RenderData.sct.hiAirway.set(g.group, target);
                        }
                    }
                }
            } else if (airwayReferFormat.test(line)) {
                const match = airwayReferFormat.exec(line);
                if (match != null && match.groups != undefined) {
                    const g = match.groups;
                    const referCoord1 = RenderData.sct.fix.get(g.refer1) ?? RenderData.sct.vor.get(g.refer1) ?? RenderData.sct.ndb.get(g.refer1);
                    const referCoord2 = RenderData.sct.fix.get(g.refer2) ?? RenderData.sct.vor.get(g.refer2) ?? RenderData.sct.ndb.get(g.refer2);
                    const referCoord3 = RenderData.sct.fix.get(g.refer3) ?? RenderData.sct.vor.get(g.refer3) ?? RenderData.sct.ndb.get(g.refer3);
                    const referCoord4 = RenderData.sct.fix.get(g.refer4) ?? RenderData.sct.vor.get(g.refer4) ?? RenderData.sct.ndb.get(g.refer4);
                    if (referCoord1 && referCoord2 && referCoord3 && referCoord4) {
                        const coord1 = new Coordinate();
                        coord1.latitude = referCoord1.coord.latitude;
                        coord1.longitude = referCoord2.coord.longitude;
                        const coord2 = new Coordinate();
                        coord2.latitude = referCoord3.coord.latitude;
                        coord2.longitude = referCoord4.coord.longitude;
                        const target: IGroupedLines = {
                            group: g.group,
                            lines: [
                                [
                                    coord1,
                                    coord2
                                ]
                            ]
                        }
                        if (lawReadingFlag) {
                            if (RenderData.sct.loAirway.has(g.group)) {
                                const orig = RenderData.sct.loAirway.get(g.group)!;
                                orig.lines.push(target.lines[0]);
                            } else {
                                RenderData.sct.loAirway.set(g.group, target);
                            }
                        } else if (hawReadingFlag) {
                            if (RenderData.sct.hiAirway.has(g.group)) {
                                const orig = RenderData.sct.hiAirway.get(g.group)!;
                                orig.lines.push(target.lines[0]);
                            } else {
                                RenderData.sct.hiAirway.set(g.group, target);
                            }
                        }
                    }
                }
            }
        } else if (aptReadingFlag && airportRegex.test(line)) {
            const match = airportRegex.exec(line);
            if (match != null && match.groups != undefined) {
                const g = match.groups;
                RenderData.sct.airport.set(g.icao, {
                    code: g.icao,
                    frequency: g.frequency,
                    class: g.class,
                    coord: new Coordinate().setFromStr(g.coord1).setFromStr(g.coord2),
                });
            }
        } else if (rwyReadingFlag && runwayRegex.test(line)) {
            const match = runwayRegex.exec(line);
            if (match != null && match.groups != undefined) {
                const g = match.groups;
                RenderData.sct.runway.push({
                    endPointA: g.ep1,
                    endPointB: g.ep2,
                    headingA: Number(g.h1),
                    headingB: Number(g.h2),
                    coordA: new Coordinate().setFromStr(g.coord1).setFromStr(g.coord2),
                    coordB: new Coordinate().setFromStr(g.coord3).setFromStr(g.coord4),
                    airportCode: g.code,
                    airportName: g.name,
                });
            }
        } else if (sidReadingFlag || startReadingFlag) {
            if (sidStarEntryRegex.test(line)) {
                const match = sidStarEntryRegex.exec(line);
                if (match != null && match.groups != undefined) {
                    const g = match.groups;
                    const target: IGroupedLines = {
                        group: g.group,
                        lines: [
                            [
                                new Coordinate().setFromStr(g.coord1).setFromStr(g.coord2),
                                new Coordinate().setFromStr(g.coord3).setFromStr(g.coord4)
                            ]
                        ]
                    };
                    if (sidReadingFlag) {
                        RenderData.sct.sid.set(target.group, target);
                    } else if (startReadingFlag) {
                        RenderData.sct.star.set(target.group, target);
                    }
                    activeSidStarGroup = target.group;
                }
            } else if (sidStarAppendRegex.test(line)) {
                const match = sidStarAppendRegex.exec(line);
                if (match != null && match.groups != undefined) {
                    const g = match.groups;
                    const line: Line = [
                        new Coordinate().setFromStr(g.coord1).setFromStr(g.coord2),
                        new Coordinate().setFromStr(g.coord3).setFromStr(g.coord4)
                    ];
                    if (sidReadingFlag && RenderData.sct.sid.has(activeSidStarGroup)) {
                        const orig = RenderData.sct.sid.get(activeSidStarGroup)!;
                        orig.lines.push(line);
                        RenderData.sct.sid.set(activeSidStarGroup, orig);
                    } else if (startReadingFlag && RenderData.sct.star.has(activeSidStarGroup)) {
                        const orig = RenderData.sct.star.get(activeSidStarGroup)!;
                        orig.lines.push(line);
                        RenderData.sct.star.set(activeSidStarGroup, orig);
                    }
                }
            }
        } else if (geoReadingFlag) {
            if (geoEntryRegex.test(line)) {
                const match = geoEntryRegex.exec(line);
                if (match != null && match.groups != undefined) {
                    const g = match.groups;
                    const target: ISctGeo = {
                        group: g.group,
                        items: [
                            {
                                flag: g.flag,
                                line: [
                                    new Coordinate().setFromStr(g.coord1).setFromStr(g.coord2),
                                    new Coordinate().setFromStr(g.coord3).setFromStr(g.coord4)
                                ]
                            }
                        ]
                    };
                    RenderData.sct.geo.set(g.group, target);
                    activeGeoGroup = RenderData.sct.geo.get(g.group)!;
                }
            } else if (geoAppendRegex.test(line)) {
                const match = geoAppendRegex.exec(line);
                if (match != null && match.groups != undefined) {
                    const g = match.groups;
                    const item: ILineWithFlag = {
                        flag: g.flag,
                        line: [
                            new Coordinate().setFromStr(g.coord1).setFromStr(g.coord2),
                            new Coordinate().setFromStr(g.coord3).setFromStr(g.coord4)
                        ]
                    };
                    if (activeGeoGroup != null) {
                        activeGeoGroup.items.push(item);
                    } else {
                        const target: ISctGeo = {
                            group: g.group,
                            items: [item]
                        };
                        RenderData.sct.geo.set(g.group, target);
                    }
                }
            }
        } else if (regReadingFlag) {
            if (regSingleNameEntryRegex.test(line)) {
                const match = regSingleNameEntryRegex.exec(line);
                if (match != null && match.groups != undefined) {
                    const g = match.groups;
                    const target: ISctRegion = {
                        name: g.name,
                        items: []
                    };
                    if (!RenderData.sct.region.has(g.name)) {
                        RenderData.sct.region.set(g.name, target);
                    }
                    activeRegionGroup = RenderData.sct.region.get(g.name)!;
                }
            } else if (regEntryRegex.test(line)) {
                const match = regEntryRegex.exec(line);
                if (match != null && match.groups != undefined) {
                    const g = match.groups;
                    const target: ISctRegion = {
                        name: g.name,
                        items: [
                            {
                                flag: g.flag,
                                coords: [
                                    new Coordinate().setFromStr(g.coord1).setFromStr(g.coord2)
                                ]
                            }
                        ]
                    };
                    if (!RenderData.sct.region.has(g.name)) {
                        RenderData.sct.region.set(g.name, target);
                    } else {
                        const orig = RenderData.sct.region.get(target.name)!;
                        orig.items.push(...target.items);
                        RenderData.sct.region.set(g.name, orig);
                    }
                    activeRegionGroup = RenderData.sct.region.get(g.name)!;
                }
            } else if (regItemAppendRegex.test(line)) {
                const match = regItemAppendRegex.exec(line);
                if (match != null && match.groups != undefined) {
                    const g = match.groups;
                    const target: ICoordArrayWithFlag = {
                        flag: g.flag,
                        coords: [
                            new Coordinate().setFromStr(g.coord1).setFromStr(g.coord2),
                        ]
                    };
                    if (activeRegionGroup != null) {
                        activeRegionGroup.items.push(target);
                    }
                }
            } else if (regCoordAppendRegex.test(line)) {
                const match = regCoordAppendRegex.exec(line);
                if (match != null && match.groups != undefined) {
                    const g = match.groups;
                    if (activeRegionGroup != null) {
                        activeRegionGroup.items[activeRegionGroup.items.length - 1].coords.push(new Coordinate().setFromStr(g.coord1).setFromStr(g.coord2));
                    }
                }
            }
        } else if (artccLowReadingFlag || artccHighReadingFlag || artccReadingFlag) {
            const items = line.split(' ').reverse();
            let group: string[] = [];
            let coord1 = new Coordinate();
            let coord2 = new Coordinate();
            let i = 0;
            for (const item of items) {
                if (item == '') continue;
                if (Coordinate.isCoordinateStr(item)) {
                    if (i < 2) {
                        coord2.setFromStr(item);
                    } else {
                        coord1.setFromStr(item);
                    }
                    i++;
                } else {
                    group.push(item);
                }
            }
            if (group.length > 0) {
                const target: IGroupedLines = {
                    group: group.join(' '),
                    lines: [
                        [coord1, coord2]
                    ]
                }
                if (artccLowReadingFlag) {
                    RenderData.sct.ARTCCLow.push(target);
                } else if (artccReadingFlag) {
                    RenderData.sct.ARTCC.push(target);
                } else {
                    RenderData.sct.ARTCCHigh.push(target);
                }
            } else {
                const line: Line = [coord1, coord2];
                if (artccLowReadingFlag && RenderData.sct.ARTCCLow.length > 0) {
                    RenderData.sct.ARTCCLow[RenderData.sct.ARTCCLow.length - 1].lines.push(line);
                } else if (artccReadingFlag && RenderData.sct.ARTCC.length > 0) {
                    RenderData.sct.ARTCC[RenderData.sct.ARTCC.length - 1].lines.push(line);
                } else if (artccHighReadingFlag && RenderData.sct.ARTCCHigh.length > 0) {
                    RenderData.sct.ARTCCHigh[RenderData.sct.ARTCCHigh.length - 1].lines.push(line);
                }
            }
        }
    }
    console.log('loadSctFile', RenderData.sct);
}